package com.hliushi.redis.task;

import com.hliushi.redis.config.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author llhuang10
 * @date 2021/8/14 21:18
 */
@Slf4j
@Component
public class TaskService {

    @Resource
    private RedisTemplate redisTemplate;


    /**
     * 2.定时5秒钟, 模拟微博的热度刷新(例如模拟点赞, 收藏, 评论的热度值更新)
     * 3.定时1小时合并统计 天, 周, 月的排行榜
     */
    @PostConstruct
    public void init() {
        log.info("启动初始化............");
        // 2.定时5秒钟, 模拟微博的热度刷新(例如模拟点赞, 收藏, 评论的热度值更新)
        new Thread(() -> this.refreshDataHour()).start();
        // 3.定时1小时合并统计 天, 周, 月的排行榜
        new Thread(() -> this.refreshData()).start();
    }


    /**
     * 定时1小时合并统计天, 周, 月的排行榜
     */
    private void refreshData() {
        while (true) {
            // 刷新当天的统计数据
            this.refreshDay();
            // 刷新7天的统计数据
            this.refreshWeek();
            // 刷新30天的统计数据
            this.refreshMonth();

            try {
                Thread.sleep(1000 * 60 * 60);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 刷新30天的统计数据
     */
    private void refreshMonth() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);

        List<String> otherKeys = new ArrayList<>();
        // 算出最近7天的内的key
        for (int i = 1; i <= (30 * 24 - 1); i++) {
            String key = Constants.HOUR_KEY + (hour - i);
            otherKeys.add(key);
        }

        // 把当前的时间key，并且把后推24*30个小时，共计近24*30小时，求出并集存入Constants.MONTH_KEY中
        // redis zunionstroe 命令求并集
        redisTemplate.opsForZSet().unionAndStore(Constants.HOUR_KEY + hour, otherKeys, Constants.MONTH_KEY);
        log.info("月刷新完成.........");
    }

    /**
     * 刷新7天的统计数据
     */
    private void refreshWeek() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);

        List<String> otherKeys = new ArrayList<>();
        // 算出最近7天的内的key
        for (int i = 1; i <= (7 * 24 - 1); i++) {
            String key = Constants.HOUR_KEY + (hour - i);
            otherKeys.add(key);
        }

        // 把当前的时间key，并且把后推24*7-1个小时，共计近24*7小时，求出并集存入Constants.WEEK_KEY中
        // redis zunionstroe 命令求并集
        redisTemplate.opsForZSet().unionAndStore(Constants.HOUR_KEY + hour, otherKeys, Constants.WEEK_KEY);
        log.info("周刷新完成.........");
    }


    /**
     * 刷新当天的统计数据
     */
    private void refreshDay() {
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);

        List<String> otherKeys = new ArrayList<>();
        // 算出最近23小时的内的key + 当前小时 = 共计 24个小时
        for (int i = 1; i <= 23; i++) {
            String key = Constants.HOUR_KEY + (hour - i);
            otherKeys.add(key);
        }

        // 把当前的时间key, 并且把后推23个小时, 共计近24个小时, 求出并集存入Constants.DAY_KEY中
        // redis zunionstroe 命令求并集
        redisTemplate.opsForZSet().unionAndStore(Constants.HOUR_KEY + hour, otherKeys, Constants.DAY_KEY);

        // 设置当前的key 40天过期, 不然历史数据浪费内容
        for (int i = 0; i < 24; i++) {
            String key = Constants.HOUR_KEY + (hour - i);
            redisTemplate.expire(key, 40, TimeUnit.DAYS);
        }
        log.info("天刷新完成.........");
    }


    /**
     * 采用26个英文字母作为帖子内容来实现排序, 随机为每个字母生成一个随机数作为score
     */
    private void refreshHour() {
        // 计算当前的小时key
        long hour = System.currentTimeMillis() / (1000 * 60 * 60);
        Random random = new Random();
        for (int i = 1; i <= 26; i++) {
            // 使用redis的 zincrby 新增加这个热度值
            redisTemplate.opsForZSet().incrementScore(Constants.HOUR_KEY + hour, String.valueOf((char) (96 + i)),
                    random.nextInt(10));
        }
    }


    /**
     * 2.定时5秒钟, 模拟微博的热度刷新(例如模拟点赞, 收藏, 评论的热度值更新)
     */
    private void refreshDataHour() {
        while (true) {
            this.refreshHour();
            // TODO 在分布式系统中, 建议用xxljob来实现定时
            // 定时5秒钟
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
